#include "Matriz.h"
#include <math.h>

#define PI 3.14159265

using namespace std;

double producto_cartesiano(std::vector<double> a, std::vector<double> b);

#define _PI = 3.14
//public:

Matriz::Matriz():_matrix(0)
{
}

Matriz::Matriz(unsigned int cant_filas, unsigned int cant_columnas)
{
    _filas = cant_filas;
    _columnas = cant_columnas;
    _matrix = new double*[_filas];
    for(unsigned int i = 0; i < _filas; i++)
        _matrix[i] = new double[_columnas];
}

Matriz::Matriz(const Matriz &matriz)
{
    copiar_matriz(matriz);
}

Matriz::Matriz(double **matrix, unsigned int filas, unsigned int columnas):_matrix(matrix), _filas(filas), _columnas(columnas){
}

Matriz::~Matriz()
{
    borrar_matriz();
}

Matriz& Matriz::operator=(Matriz matriz)
{
	//cerr << "OPERATOR =" << endl << matriz._filas <<" " << matriz._columnas << endl;
    if(this != &matriz)
    {
        borrar_matriz();
        copiar_matriz(matriz);
    }
}

unsigned int Matriz::cantFilas()const
{
	return _filas;
}

unsigned int Matriz::cantColumnas() const
{
	return _columnas;
}

std::vector<double> Matriz::iesima_fila(unsigned int i) const
{
	std::vector<double> retorno(_columnas);
	for(int j = 0; j < _columnas; j++)
		retorno[j] = _matrix[i][j];
	return retorno;
}

std::vector<double> Matriz::iesima_columna(unsigned int i) const
{
	std::vector<double> retorno(_filas);
	for(int j = 0; j < _filas; j++)
		retorno[j] = _matrix[j][i];
	return retorno;
}

double Matriz::valor(unsigned int fila, unsigned int columna) const {
	return _matrix[fila][columna];
}

Matriz Matriz::operator+(const Matriz &otra) const
{
	Matriz ret(*this);
	for(int i = 0; i < _filas; i++)
		for(int j = 0; j < _columnas; j++)
			ret._matrix[i][j] += otra._matrix[i][j];
	return ret;
}

Matriz Matriz::operator*(const Matriz &otra) const
{
	Matriz ret(_filas, otra._columnas);
	//cerr << "OPERATOR *" << endl << ret._filas <<" " << ret._columnas << endl;
	for(int i = 0; i < ret._filas; i++)
		for(int j = 0; j < ret._columnas; j++)
			ret._matrix[i][j] = producto_cartesiano(iesima_fila(i), otra.iesima_columna(j));
			
	//cerr << "RET : " << endl << ret << endl;
	return ret;
}

std::vector<double> Matriz::operator*(const std::vector<double> &vec) const
{
    std::vector<double> resultado(_filas);
    for(int i = 0; i < _filas; i++)
        resultado[i] = producto_cartesiano(iesima_fila(i), vec);
    
    return resultado;
}

Matriz Matriz::trasponer()
{
	Matriz traspuesta(_filas, _columnas);
	for(int fila = 0; fila < _filas; fila++)
		for(int col = 0; col < _columnas; col++)
			traspuesta._matrix[fila][col] = this->_matrix[col][fila];
	return traspuesta;
}

std::ostream& operator<<(std::ostream &os, const Matriz &matriz)
{
	for(int i = 0; i < matriz._filas; i++)
	{
		for( int j = 0; j < matriz._columnas; j++)
			os << matriz._matrix[i][j] << "\t";

		os << std::endl;
	}
	return os;
}
	
//private:
    
void Matriz::copiar_matriz(const Matriz &matriz)
{
	_filas = matriz._filas;
	_columnas = matriz._columnas;
	_matrix = new double*[_filas];
	for(int i = 0; i < _filas; i++)
		_matrix[i] = new double[_columnas];

	for (int fila = 0; fila < _filas; fila++)
	{
		for(int col = 0; col < _columnas; col++)
		{
			_matrix[fila][col] = matriz._matrix[fila][col];

		}
	}
}

void Matriz::borrar_matriz()
{
    if (_matrix != 0)
    {
        for (int i = 0; i < _filas; i++)
            if(_matrix[i] != 0)
            {
              delete _matrix[i];
              _matrix[i] = 0;
            }

        delete _matrix;
        _matrix = 0;
    }
}

Matriz Matriz::IDENTIDAD(unsigned int n)
{
	Matriz identidad(n, n);
	for(int i = 0; i < n; i++)
		for(int j = 0; j < n; j++)
			identidad._matrix[i][j] = (i == j)? 1 : 0;
	return identidad;
}

//funciones agregadas
double producto_cartesiano(std::vector<double> a, std::vector<double> b)
{
	double ret = 0;
	for(int i = 0; i < a.size(); i++)
		ret += a[i]*b[i];
	return ret;
}
